/**
 * 
 */
package com.inkcar.generator;

import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.inkcar.freemaker.FreemarkerRender;
import com.inkcar.generator.model.Config;
import com.inkcar.generator.model.Table;
import com.inkcar.generator.model.TableProperty;
import com.inkcar.generator.model.Template;
import com.inkcar.utils.FileUtils;
import com.inkcar.utils.JSONUtils;
import com.inkcar.utils.StringUtils;

/**
 * 对象业务生成入口类
 * 
 * @author 欧阳增高
 * 
 */
public class Generator {
	/**
	 * 私有化构造
	 */
	private Generator() {
	}

	/**
	 * 实例化
	 */
	private static Generator gen = new Generator();

	/**
	 * 数据表实体缓存
	 */
	private Set<Table> TABLE_CACHE;

	/**
	 * jdbc类型对应java类型
	 */
	private static Map<String, String> CACHE_PROPERTY_TYPE;
	private static Map<String, String> CACHE_PROPERTY_TYPE_CONVERT;
	static {
		CACHE_PROPERTY_TYPE = new HashMap<String, String>();
		CACHE_PROPERTY_TYPE.put("varchar", "String");
		CACHE_PROPERTY_TYPE.put("nvarchar", "String");
		CACHE_PROPERTY_TYPE.put("text", "String");
		CACHE_PROPERTY_TYPE.put("integer", "Integer");
		CACHE_PROPERTY_TYPE.put("int", "Integer");
		CACHE_PROPERTY_TYPE.put("tinyint", "Integer");
		CACHE_PROPERTY_TYPE.put("smallint", "Integer");
		CACHE_PROPERTY_TYPE.put("double", "Double");
		CACHE_PROPERTY_TYPE.put("float", "Float");
		CACHE_PROPERTY_TYPE.put("long", "Long");
		CACHE_PROPERTY_TYPE.put("bigint", "Long");
		CACHE_PROPERTY_TYPE.put("char", "Character");
		CACHE_PROPERTY_TYPE.put("boolean", "Boolean");
		CACHE_PROPERTY_TYPE.put("decimal", "java.math.BigDecimal");
		CACHE_PROPERTY_TYPE.put("datetime", "java.util.Date");
		CACHE_PROPERTY_TYPE.put("date", "java.util.Date");
		CACHE_PROPERTY_TYPE.put("time", "java.util.Date");
		CACHE_PROPERTY_TYPE.put("timestamp", "java.util.Date");
		CACHE_PROPERTY_TYPE.put("enum", "enum");

		CACHE_PROPERTY_TYPE_CONVERT = new HashMap<String, String>();
		CACHE_PROPERTY_TYPE_CONVERT.put("text", "longvarchar");
		CACHE_PROPERTY_TYPE_CONVERT.put("int", "integer");
	}

	/**
	 * 渲染模板数据
	 * 
	 * @param cfg
	 *            配置实体对象
	 * @param tmp
	 *            模板实体对象
	 * @param table
	 *            数据表及对应JAVA配置
	 */
	private void render(Config cfg, Template tmp, JSONArray tbs) {
		String templatePath = tmp.getTemplatePath();
		// 通过类路径获取模板文件路径
		URL path = this.getClass().getResource(templatePath);
		if (path != null) {
			templatePath = path.getFile();
		}

		// 读取模板内容
		String templateConext = FileUtils.readText(templatePath);
		if (StringUtils.isEmpty(templateConext)) {
			return;
		}

		try {
			// 设置当前模板缓存
			FreemarkerRender.setTemplate(tmp.getTemplateName(), templateConext);

			// 如模板中未设置包路径，则以模板名代替
			if (StringUtils.isEmpty(tmp.getPackageName())) {
				tmp.setPackageName("." + tmp.getTemplateName().toLowerCase());
			}

			// 组装模板渲染数据
			Map<String, Object> target = new HashMap<String, Object>();
			target.put("entity", tmp);
			target.put("config", cfg);

			// 遍历数据库表
			for (Table t : TABLE_CACHE) {
				// 组装表实体包路径
				StringBuffer pack = new StringBuffer(cfg.getPackageName());
				if (!StringUtils.isEmpty(t.getPackageName())) {
					pack.append(t.getPackageName());
				}
				pack.append(tmp.getPackageName());

				// 组装并创建包文件夹
				StringBuffer savePath = new StringBuffer();

				if (StringUtils.isEmpty(tmp.getSavePath())) {
					savePath.append(cfg.getSavePath());
					savePath.append(pack.toString().replaceAll("\\.", "/"));
					savePath.append("/");
				} else {
					savePath.append(tmp.getSavePath());
					if (".java".equals(tmp.getExtension())) {
						savePath.append(pack.toString().replaceAll("\\.", "/"));
					} else if (!StringUtils.isEmpty(t.getPackageName())) {
						savePath.append(t.getPackageName().replaceAll("\\.",
								"/"));
					}
					savePath.append("/");
				}
				System.out.println(savePath);

				// 创建保存文件夹
				FileUtils.createFolders(savePath.toString());

				// 添加表实体数据到模板渲染MAP中
				target.put("table", t);

				// 渲染模板
				String code = FreemarkerRender.renderToName(target,
						tmp.getTemplateName());

				// 组装保存文件
				if (!StringUtils.isEmpty(tmp.getPrefix())) {
					savePath.append(tmp.getPrefix());
				}
				savePath.append(t.getEntityName());
				if (!StringUtils.isEmpty(tmp.getSuffix())) {
					savePath.append(tmp.getSuffix());
				}
				savePath.append(tmp.getExtension());
				System.out.println(savePath);

				// 创建生成文件
				FileUtils.create(savePath.toString(), code);
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取数据表格配置
	 * 
	 * @param tableCfg
	 *            配置路径数组
	 * @return Set
	 *         <Table>
	 */
	private Set<Table> getTable(JSONArray tableCfg) {
		Set<Table> tbl = new LinkedHashSet<Table>();

		// 遍历数据库表结构配置
		for (Object path : tableCfg) {
			if (StringUtils.isEmpty(path)) {
				continue;
			}
			String tblCfgPath = path.toString();
			// 通过类路径获取模板文件路径
			URL dataPath = this.getClass().getResource(tblCfgPath);
			if (dataPath != null) {
				tblCfgPath = dataPath.getFile();
			}

			// 读取表配置
			String text = FileUtils.readText(tblCfgPath);
			if (StringUtils.isEmpty(text)) {
				continue;
			}
			// json转换
			JSONObject group = JSONUtils.toJSON(text);
			String packageName = group.getString("packageName");
			JSONArray arr = group.getJSONArray("table");

			// 添加到Set中待用
			tbl.addAll(this.initTableProperty(arr, packageName));
		}
		return tbl;
	}

	/**
	 * 初始转换表实体对象
	 * 
	 * @param table
	 *            配置文件中设置的表配置
	 * @param packageName
	 *            数据表组所属包
	 * @return Set
	 *         <Table>
	 */
	private Set<Table> initTableProperty(JSONArray table, String packageName) {
		Set<Table> tables = new LinkedHashSet<Table>();
		// 遍历表配置
		for (int i = 0, len = table.size(); i < len; i++) {
			JSONObject jsonTable = table.getJSONObject(i);
			if (jsonTable == null || jsonTable.size() == 0
					|| !jsonTable.containsKey("tableName")) {
				continue;
			}
			JSONArray fields = jsonTable.getJSONArray("columns");
			if (fields == null || fields.size() == 0) {
				continue;
			}

			Table tb = JSON.toJavaObject(jsonTable, Table.class);

			// 检查数据表格包路径
			if (!StringUtils.isEmpty(packageName)) {
				tb.setPackageName(packageName);
			}
			TableProperty primary = null;

			Set<TableProperty> pros = new LinkedHashSet<TableProperty>();
			Set<TableProperty> sequences = new LinkedHashSet<TableProperty>();
			for (Object f : fields) {
				// 转换为数据表属性实例
				TableProperty field = JSON.toJavaObject((JSONObject) f,
						TableProperty.class);
				if (field == null || StringUtils.isEmpty(field.getName())) {
					continue;
				}
				// 设置生成实体属性名
				if (StringUtils.isEmpty(field.getProperty())) {
					String tmpField = field.getName();
					if ('_' == tmpField.charAt(1)) {
						tmpField = tmpField.substring(2);
					}
					field.setProperty(StringUtils
							.Tokenizer(tmpField, "_", true));
					if (tmpField.indexOf("_") != -1) {
						field.setOrignProperty(tmpField);
					}
				}
				// 设置生成实体类型
				if (StringUtils.isEmpty(field.getPropertyType())) {
					field.setPropertyType(CACHE_PROPERTY_TYPE.get(field
							.getType()));
				}

				field.setConvertType(CACHE_PROPERTY_TYPE_CONVERT.get(field
						.getType()));
				field.setType(field.getType());

				// 设置主键
				if (primary == null) {
					primary = field;
					continue;
				}

				/**
				 * 检查是否为自动顺号定义
				 */
				if (!StringUtils.isEmpty(field.getSequenceCode())) {
					sequences.add(field);
				}

				pros.add(field);
			}
			tb.setPropertys(pros);

			// 添加自定义序号字段
			if (sequences.size() > 0) {
				tb.setSequences(sequences);
			}

			tb.setPrimaryProperty(primary);
			tables.add(tb);
		}
		return tables;
	}

	/**
	 * 构建实体及相关文件
	 * 
	 * @param conf
	 *            生成配置文件路径
	 */
	private void build(String conf) {
		if (StringUtils.isEmpty(conf)) {
			conf = this.getClass().getResource("/test.json").getFile();
		}
		if (StringUtils.isEmpty(conf)) {
			return;
		}
		// 读取配置文件
		JSONObject json = JSONUtils.toJSON(FileUtils.readText(conf));
		Config cfg = new Config(json.getString("packageName"),
				json.getString("savePath"));

		// 获取表配置
		JSONArray table = json.getJSONArray("table");

		JSONArray tableConfig = json.getJSONArray("tableConfig");

		TABLE_CACHE = new LinkedHashSet<Table>();

		// 初始化表数据转换
		if (table != null && table.size() > 0) {
			TABLE_CACHE = this.initTableProperty(table, null);
		}

		/**
		 * 从单独表结构配置中读取数据库配置
		 */
		if (tableConfig != null && tableConfig.size() > 0) {
			TABLE_CACHE.addAll(this.getTable(tableConfig));
		}

		// 获取模板配置
		JSONArray template = json.getJSONArray("template");

		// 遍历模板数据
		for (Object js : template) {
			Template tmp = JSON.toJavaObject((JSONObject) js, Template.class);
			if (tmp == null || StringUtils.isEmpty(tmp.getTemplatePath())) {
				continue;
			}
			this.render(cfg, tmp, table);
		}

	}

	/**
	 * 命令行调用入口
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		String conf = null;
		if (args != null && args.length > 0) {
			conf = args[0];
		}
		gen.build(conf);
	}

}
